class ReportResponse < ActiveRecord::Base

  belongs_to :owner_role, :class_name => 'Role'
  belongs_to :report_request
  
  has_one :report, :through => :report_request
  has_one :requestor, :through => :report_request, :source => :requestor

  has_many :answers
  has_many :report_edits
  
  before_create :set_defaults
  after_update :calculate_completed
  
  accepts_nested_attributes_for :answers, 
    :reject_if => proc { |att| att['option_id'].blank? and att['value_text'].blank? }
  
  validates_associated :answers
  
  def owner_users
    self.owner_role.users
  end
  
  # Returns all unsubmitted report responses for the specified role
  def self.get_pending(role_or_roles)
      if role_or_roles.respond_to? :each 
        role_or_roles.map(&self.method(:get_pending)).flatten
      else
        self.find_all_by_owner_role_id_and_is_submitted role_or_roles.id, false
      end
  end
  
  # Returns all submitted report responses for the specified role
  def self.get_completed(role)
    self.find_all_by_owner_role_id_and_is_submitted role.id, true
  end
  
  def self.get_all_completed
    self.find_all_by_is_submitted true
  end

  # Returns the status of a given country
  
  def self.get_country_xb_status(role, options = {})
    if options[:pending]
      role.coordinated_xbs.map{|r| r.report_responses}.flatten.select{|x| not x.is_submitted }
    elsif options[:completed]
      role.coordinated_xbs.map{|r| r.report_responses}.flatten.select{|x| x.is_submitted }
    elsif options[:not_submitted]
      pending_requests = ReportResponse.get_pending(role).map{|r| r.report_request}
      role.coordinated_xbs.map{|r| r.report_responses}.flatten.select{|r| pending_requests.include?(r.report_request)}
    else
      role.coordinated_xbs.map{|r| r.report_responses}.flatten
    end
  end
  
  # Returns the report response with the specified id and ensures the user is its owner
  def self.find_by_id_for_user(id, user)
    response = ReportResponse.find_by_id id
    response unless !response.owner_users..include?(user)
  rescue
    nil
  end
  
  #Returns the report responses for all strategy coordinator except mine
  def find_by_type(type)
    repoResponses = {}
    roles = Role.find_all_by_type(type)
    roles.each do |r|
      strategy=r.strategy 
      repoRespo=ReportResponse.find_by_owner_role_id_and_report_request_id r.id, self.report_request.id
      repoResponses[strategy]=repoRespo
    end
    
#    roles.length.times do |i|
#      id=roles[i].id
#      strategy=roles[i].coordinated.id
#      repoRespo=ReportResponse.find_by_owner_role_id id #and report_request_id
#      repoResponses[strategy]=repoRespo
#    end
    return repoResponses
  end
  
  # Returns the answer, if exists, for a specified question within this report
  def answer(question, site = nil)
    if site.nil? then 
      self.answers.select {|answer| answer.question == question}.first
    else
      self.answers.select {|answer| answer.question == question and answer.site == site}.first
    end
  end
  
  # Returns the answer for a specified question within this report or a new unsaved answer
  def answer_or_default(question, site = nil)
    self.answer(question, site) || Answer.create(:question => question, :report_response => self, :site => site)
  end
  
  def answer_of_xb (question, role_xb)
    rep_req = self.report_request
    rep_res_xb = rep_req.report_responses.find_by_owner_role_id role_xb.id
    begin
      answer = rep_res_xb.answers.find_by_question_id question.id
      answer = Answer.new(:question => question) if answer.nil?
      return answer
    rescue Exception => exc
      logger.error("Error ocured in report_response method answer_of_xb #{exc.message}")
      return nil
    end  
  end
  
  def xb_answer(question, xb)
    if question.for_role(self.owner_role).xb_filled_by_cc?
      answer(question, xb.site)
    elsif !question.for_role(xb).nil?
      answer_of_xb(question, xb)
    else
      return nil
    end
  end
  
  def cc_answer(question)
    if question.for_role(self.owner_role).first_column_auto?
      nil
    else
      answer_or_default(question)
    end
  end
  
  # Returns the array of Question that apply for the current role
  # TODO: Check performance of reject condition! Move to SQL.
  def questions
    self.report.questions.reject { |q| q.for_role(self.owner_role).nil? }
  end

  # Returns the ReportResponse corresponding to the matching xb if this is an xb response
  def matching_xbs_completed
    site = self.owner_role.coordinated
    res = 0.0
    
    site.matchings.each do |other_site|      
      matching_role = other_site.coordinator_role
      response = ReportResponse.find_by_report_request_id_and_owner_role_id self.report_request.id, matching_role.id
      res += response.completed
    end
    
    safety_number_to_percentage(res/site.matchings.size)
  rescue
    nil
  end
  
  def matching_site_names
    self.owner_role.coordinated.matching_names
  rescue
    nil
  end
  
  # Updates completed % of answers straight to db
  def update_completed
    logger.info "Updating all report responses. completed = #{self.get_completed}, id => #{self.id}."
    ReportResponse.update_all("completed = #{self.get_completed}", { :id => self.id })
  end
  
  def previous_response()
    self.class.first(:conditions => ["id < ? AND owner_role_id = ? AND is_submitted = ?", self.id, self.owner_role.id, self.is_submitted], :order => "id desc")
  end
 
  def next_response()
    self.class.first(:conditions => ["id > ? AND owner_role_id = ? AND is_submitted = ?", self.id, self.owner_role.id, self.is_submitted], :order => "id asc")
  end
  
  # TODO: Optimize by using SQL counts with joins
  def get_completed
    # calculate completed for all questions
    get_completed_for_questions(self.report.questions)
  end
  
  def get_completed_for_strategy(strategy)
    get_completed_for_questions(strategy.grouping.questions)
  end
  
  def get_completed_for_questions(questions)
    # Get questions available for the owner role
    questions = questions.select{|q| q.available_for(self.owner_role)}
    
    # If the owner role is a SC, then reject questions that don't belong to the strategy of the SC
    if self.owner_role.is_strategy_coordinator
      questions.reject!{|q| q.grouping.strategy != self.owner_role.strategy}
    end
    
    q_count = calculate_questions_count(questions)
    
    # Count answers
    a = 0
    self.answers.each do |answer|
      question = answer.question                                 
      # Dont consider this answer if its question isnt in the questions list
      next unless questions.include?(question)
      a += calculate_answer_weight(answer)
    end
    
    # Calculate percentage
    # when no questions need to be filled, returns 100%
    if q_count == 0 then 1 else Float(a) / Float(q_count) end
  end
  
  def completed_description
    safety_number_to_percentage(self.completed)
  end
  
  def count_yes_answers
    count =0
    self.answers.each do |answer|
       if(answer.is_yes)
         count = count +1
       end
    end
    count
  end
  
   def count_yes_answers_for_group(group)
    count =0
    self.answers.each do |answer|
       if(answer.question.grouping == group and answer.is_yes)
         count = count +1
       end
    end
    count
  end
  
   def count_no_answers_for_group(group)
    count =0
    self.answers.each do |answer|
       if(answer.question.grouping == group and answer.is_no)
         count = count +1
       end
    end
    count
  end
  
  
  def count_no_answers
    count =0
    self.answers.each do |answer|
       if(answer.is_no)
         count = count +1
       end
    end
    count
  end
  
  private
  
  def safety_number_to_percentage(num)
    # force something less than 100% when it is not completed
    res = '%.0f%' % (num * 100)
    if res == '100%' && num < 1
      res = '99%'
    end

    res
  end
  
  def calculate_answer_weight(answer)
    # the xb_filled_by_cc? answers generate more than one answer
    # no special treatment is needed
    if answer.question.is_option
      res = answer.question_options.size
      other = answer.answers_question_options.select{|a| a.question_option.is_other}.first
      unless other.nil?
        # if other is No or has a description, add 1.
        # the description is stored in answer.value_text
        if !other.state || !answer.value_text.empty?
          res = res + 1
        end
      end
      res
    else
      (answer.option.nil? ? 0 : 1) + (answer.value_text.blank? ? 0 : 1)
    end
  end
  
  def calculate_questions_count(questions)
    # is_text count as 1. is_excluding, is_yesno count as 2, one for answer, one for description
    # is_option doesn't support description, they count as the number of options available
    
    # each of those must consider if the owner_role.is_contry_coordinator
    # and if it has xb_filled_by_cc? because that means that has to be filled
    # also one per each cross border
    
    # other's description field is required, then the options
    # should weight one more, and answere would be Yes=1, No=2, Description=1
    res = 0
    
    if self.owner_role.is_country_coordinator
      xb_count = self.owner_role.coordinated_xbs.size
    end 
    
    questions.each do |q|
      if q.is_option
        q_weight = q.options.size + ((q.options.any? &:is_other) ? 1 : 0)
      else
        q_weight = (q.is_text ? 1 : 2)
      end
      
      if self.owner_role.is_country_coordinator && q.for_role(self.owner_role).xb_filled_by_cc?
        res += q_weight * (1 + xb_count)
      else
        # owner_role is not country coordinator
        # or the question is not :xb_filled_by_cc?
        # just use the current question weight                
        res += q_weight
      end
    end
    
    res
  end
  
  def calculate_completed
    self.completed = get_completed
    self.send(:update_without_callbacks)
  ensure
    true
  end
  
  def set_defaults
    self.is_submitted = false if self.is_submitted.nil?
    true
  end
  
end
